import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Vector;
import org.htmlparser.Node;
import org.htmlparser.Parser;
import org.htmlparser.Tag;
import org.htmlparser.filters.HasAttributeFilter;
import org.htmlparser.filters.TagNameFilter;
import org.htmlparser.tags.ScriptTag;
import org.htmlparser.tags.StyleTag;
import org.htmlparser.util.EncodingChangeException;
import org.htmlparser.util.NodeList;
import org.htmlparser.util.ParserException;
import org.htmlparser.util.SimpleNodeIterator;

/**
 * @author ofer
 *
 */
public class ScriptExtractor2 {
	private Parser parser;
	private AttrTable attr = null;
	private EventTable events = null;
	private TagsTable tags = null;
	private boolean addFlag = false;
	private ScriptsDB2.AppMode mode;
	private Vector<ScriptNode> scriptNodeList = null;
	private Vector<Tag> scriptTagList = null;


	/**
	 * Constructor
	 * @param url
	 * @throws ParserException
	 */
	public ScriptExtractor2(String url,ScriptsDB2.AppMode mode) throws ParserException{
		this.parser = new Parser(url);
		parser.reset();
		this.attr = AttrTable.getInstance();
		this.events = EventTable.getInstance();
		this.tags = TagsTable.getInstance();
		this.mode = mode;
	}

	public ScriptExtractor2(Parser parser){
		this.parser = parser;
		parser.reset();
		this.mode = ScriptsDB2.AppMode.LEARN;
		this.attr = AttrTable.getInstance();
		this.events = EventTable.getInstance();
		this.tags = TagsTable.getInstance();
	}

	private boolean CheckContent(Tag tag){
		String val = null ; 
		boolean isJsFile = false;
		boolean isCssFile = false;
		boolean isUri = false;
		boolean isExpress = false;
		if (tag instanceof AttrTag)
			val = ((AttrTag)tag).getValue();
		else if (tag instanceof StyleTag)
			val = ((StyleTag)tag).getStringText();
		if (val!=null){
			isJsFile = (val.matches(".*\\.js[?]{0,1}"));
			isCssFile = (val.matches(".*\\.css[?]{0,1}"));
			isUri = (val.matches(".*j[.\\s]*a[.\\s]*v[.\\s]*a[.\\s]*s[.\\s]*c[.\\s]*r[.\\s]*i[.\\s]*p[.\\s]*t[.\\s]*:.*"));
			isExpress = (val.matches(".*e[.\\s]*x[.\\s]*p[.\\s]*r[.\\s]*e[.\\s]*s[.\\s]*s[.\\s]*i[.\\s]*o[.\\s]*n[.\\s]*\\(.*"));
		}
			return ( (isJsFile || isCssFile || isUri || isExpress) && (!scriptTagList.contains(tag)));
	}

	/**
	 * 
	 * @return Vector<ScriptNode>
	 * @throws ParserException
	 * @throws DetectorException
	 * @throws IOException
	 */
	public Vector<ScriptNode> getScriptNodes() 
	throws ParserException, DetectorException, IOException {

		Node curr = null;
		String attrVal = null;
		NodeList nl = new NodeList();
		scriptTagList = new Vector<Tag>();
		scriptNodeList = new Vector<ScriptNode>();
		EventTag eve = null;
		AttrTag atr = null;
		ScriptTag scr = null;
		ScriptNode sn = null;
		NodeList allNodes = null;
		boolean isJS  =false;

		try{
			allNodes = parser.parse(null);
		}catch (EncodingChangeException e){
			parser.reset();
			allNodes = parser.parse(null);
		}

		allNodes = allNodes.extractAllNodesThatMatch(
				new TagNameFilter("html"),true);

		//extract all <script> tags
		for (int i=0;i<tags.getTable().size();i++){
			String type = tags.getTable().elementAt(i);
			nl = (allNodes.extractAllNodesThatMatch(new TagNameFilter(type),true));
			SimpleNodeIterator ni = nl.elements();
			while (ni.hasMoreNodes()){
				curr = ni.nextNode();
				if (type.equals("script"))
					scriptTagList.add((ScriptTag)curr);
				else if (type.equals("style")){
					String t = ((StyleTag)curr).getAttribute("type");
					if (t!=null)
						  isJS= (t.toLowerCase().indexOf("javascript") >-1 );
					//save in the cases it might fire a JS
					if (CheckContent((StyleTag)curr) || isJS)
						scriptTagList.add((StyleTag)curr);
					
				}
			}
		}
		int s = scriptTagList.size();
		System.out.println("Found " + s + " script Tags");

		for (int i=0;i<attr.getTable().size();i++){
			String type = attr.getTable().elementAt(i);
			nl = (allNodes.extractAllNodesThatMatch(new HasAttributeFilter(type),true));
			SimpleNodeIterator ni = nl.elements();
			while (ni.hasMoreNodes()){
				curr = ni.nextNode();
				atr = new AttrTag ((Tag)curr,type);
				attrVal = atr.getValue();
				if (attrVal==null)
					continue;
				else
					attrVal=attrVal.toLowerCase();
				if (CheckContent(atr))
					scriptTagList.add(atr);
			}
		}
		System.out.println("Found " + (scriptTagList.size()-s) + " attr Tags");
		s=scriptTagList.size();

		for (int i=0;i<events.getTable().size();i++){
			String type = events.getTable().elementAt(i);
			nl = (allNodes.extractAllNodesThatMatch(new HasAttributeFilter(type),true));
			SimpleNodeIterator ni = nl.elements();
			while (ni.hasMoreNodes()){
				curr = ni.nextNode();
				eve = new EventTag((Tag)curr,type);
				if (!scriptTagList.contains(curr))
					scriptTagList.add(eve);
			}
		}
		System.out.println("Found " + (scriptTagList.size()-s) + " event Tags");
		int counter=1;
		for (Tag tag : scriptTagList){
			//System.out.println("Handling script number " + counter);
			sn = new ScriptNode(tag,mode);
			scriptNodeList.add(sn);
			counter+=1;
		}

		return scriptNodeList;
	}

	/**
	 * For test uses only
	 * @param args
	 */
	public static void main(String[] args) {

		Vector <String> urls = new Vector<String>();
		Vector<ScriptNode> pageScripts = null;
		try {
			FileReader fr =new FileReader(new File(args[0]));
			BufferedReader br = new BufferedReader(fr);
			StringBuilder sb = new StringBuilder();

			String curr = br.readLine();

			while (curr!=null){
				urls.add(curr);
				//sb.append(curr + "\n\r");
				curr = br.readLine();
			}

			new ScriptNode(sb.toString());

			for (String url : urls){
				System.out.println("Checking " + url);
				pageScripts = new ScriptExtractor2(url,ScriptsDB2.AppMode.LEARN).getScriptNodes();
			}
		} catch (ParserException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (DetectorException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
